﻿namespace PugLib.UI
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Windows.Forms;

    public sealed class FormFactory
    {
        private static Singleton<Form, FormFactory> _instance;
        private readonly Form _formCreatedOnMainUIThread;

        public static void Bind(Form formCreatedOnMainUIThread)
        {
            _instance = new Singleton<Form, FormFactory>(CreateInstance, formCreatedOnMainUIThread);
        }

        [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Generic return type")]
        public static TForm Create<TForm>() where TForm : Form, new()
        {
            if (_instance == null)
            {
                throw new InvalidOperationException(Strings.NoOwnerErrorMessage);
            }
            return _instance.Value.CreateForm<TForm>();
        }

        public static DialogResult ShowDialog(Form owner, Form child)
        {
            return owner.InvokeFormShowDialogIfRequired(child.ShowDialog);
        }

        public static DialogResult ShowDialog(Form child)
        {
            if (_instance == null)
            {
                throw new InvalidOperationException(Strings.NoOwnerErrorMessage);
            }
            return _instance.Value._formCreatedOnMainUIThread.InvokeFormShowDialogIfRequired(child.ShowDialog);
        }

        public static void Show(Form owner, Form child)
        {
            owner.InvokeFormShowIfRequired(child.Show);
        }

        #region private

        private FormFactory(Form formCreatedOnMainUIThread)
        {
            _formCreatedOnMainUIThread = formCreatedOnMainUIThread;
        }

        private static FormFactory CreateInstance(Form formCreatedOnMainUIThread)
        {
            return new FormFactory(formCreatedOnMainUIThread);
        }

        private TForm CreateForm<TForm>() where TForm : Form, new()
        {
            Func<TForm> make = CreateInternal<TForm>;
            return _formCreatedOnMainUIThread.InvokeIfRequired(make);
        }

        private static TForm CreateInternal<TForm>() where TForm : Form, new()
        {
            return new TForm();
        }

        #endregion private
    }

    public static class FormExtensions
    {
        public static T InvokeIfRequired<T>(this Control control, Func<T> action)
        {
            if (control.InvokeRequired)
            {
                T rtn = (T)control.Invoke(action);
                return rtn;
            }
            return action();
        }

        public static DialogResult InvokeFormShowDialogIfRequired(this Form owner, Func<IWin32Window, DialogResult> childShowDialog)
        {
            if (owner.InvokeRequired)
            {
                DialogResult rtn = (DialogResult)owner.Invoke(childShowDialog, owner);
                return rtn;
            }
            return childShowDialog(owner);
        }

        public static void InvokeFormShowIfRequired(this Form owner, Action<IWin32Window> childShow)
        {
            if (owner.InvokeRequired)
            {
                owner.Invoke(childShow, owner);
                return;
            }
            childShow(owner);
        }
    }
}